/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */
package xowl.interpreter.backends.rdf;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import xowl.lang.actions.Equal;
import xowl.lang.actions.Query;
import xowl.lang.actions.QueryVariable;
import xowl.lang.owl2.Axiom;
import xowl.lang.owl2.Expression;
import xowl.lang.owl2.LiteralExpression;

/**
 *
 * @author Laurent WOUTERS
 */
public class RDFRuleTranslator {
    private static final String genVarPrefix = "_link";
    
    private TranslatorXOWL_RDF translator;
    
    public RDFRuleTranslator(xowl.interpreter.IRIProvider provider, RDFGraph graph) {
        translator = new TranslatorXOWL_RDF(provider, graph);
    }

    public RDFRule translate(xowl.interpreter.impl.Evaluator evaluator, xowl.lang.rules.Rule rule) throws xowl.interpreter.XOWLException {
        Job job = new Job(evaluator, rule);
        return job.execute();
    }

    protected class Job {
        private xowl.interpreter.impl.Evaluator evaluator;
        private xowl.lang.rules.Rule original;
        private TranslatorContext context;
        private RDFRule result;
        private Integer genVarCount;

        public Job(xowl.interpreter.impl.Evaluator evaluator, xowl.lang.rules.Rule original) {
            this.evaluator = evaluator;
            this.original = original;
            this.context = new TranslatorContext();
            this.result = new RDFRule(original.getHasIRI().getHasValue());
            this.genVarCount = 0;
        }
        
        public RDFRule execute() throws xowl.interpreter.XOWLException {
            for (xowl.lang.rules.Assertion assertion : original.getAllAntecedents()) {
                List<RDFTriple> buffer = null;
                if (assertion.getIsPositive()) {
                    if (assertion.getIsMeta())
                        buffer = result.getPositiveAntecedentMeta();
                    else
                        buffer = result.getPositiveAntecedentAxioms();
                } else {
                    buffer = new ArrayList<RDFTriple>();
                    if (assertion.getIsMeta())
                        result.getNegativeAntecedentMeta().add(buffer);
                    else
                        result.getNegativeAntecedentAxioms().add(buffer);
                }
                translateAntecedent(buffer, assertion.getAllAxioms());
            }
            for (xowl.lang.rules.Assertion assertion : original.getAllConsequents()) {
                List<RDFTriple> buffer = null;
                if (assertion.getIsPositive()) {
                    if (assertion.getIsMeta())
                        buffer = result.getPositiveConsequentMeta();
                    else
                        buffer = result.getPositiveConsequentAxioms();
                } else {
                    buffer = new ArrayList<RDFTriple>();
                    if (assertion.getIsMeta())
                        result.getNegativeConsequentMeta().add(buffer);
                    else
                        result.getNegativeConsequentAxioms().add(buffer);
                }
                translateConsequent(buffer, assertion.getAllAxioms());
            }
            for (LiteralExpression exp : original.getAllGuards()) {
                result.getGuards().add(exp);
            }
            return result;
        }

        private void translateAntecedent(List<RDFTriple> buffer, Collection<Axiom> axioms) throws xowl.interpreter.XOWLException {
            Translation translation = translator.translateAxioms(evaluator, context, false, false, axioms);
            for (RDFTriple triple : translation.getTriples()) {
                RDFSubjectNode subj = (RDFSubjectNode)translateAncedentNode(triple.getSubject());
                RDFProperty prop = (RDFProperty)translateAncedentNode(triple.getProperty());
                RDFNode obj = translateAncedentNode(triple.getObject());
                buffer.add(new RDFTriple(subj, prop, obj));
            }
        }
        private void translateConsequent(List<RDFTriple> buffer, Collection<Axiom> axioms) throws xowl.interpreter.XOWLException {
            Translation t1 = translator.translateAxioms(evaluator, context, false, false, axioms);
            for (RDFTriple triple : t1.getTriples()) {
                RDFSubjectNode subj = (RDFSubjectNode)translateConsequentNode(triple.getSubject());
                RDFProperty prop = (RDFProperty)translateConsequentNode(triple.getProperty());
                RDFNode obj = translateConsequentNode(triple.getObject());
                buffer.add(new RDFTriple(subj, prop, obj));
            }
        }
        
        private RDFNode translateAncedentNode(RDFNode node) {
            if (node.getNodeType() == RDFNodeType.Dynamic) {
                String varName = genVarPrefix + genVarCount;
                genVarCount++;
                QueryVariable var = new QueryVariable();
                var.setName(varName);
                Equal exp = new Equal();
                exp.setLhs(var);
                exp.setRhs(((XOWLDynamicNode)node).getDynamicExpression());
                result.getGuards().add(exp);
                XOWLVariableNode varNode = new XOWLVariableNode(var);
                result.addAntecedentVar(varNode);
                return varNode;
            } else if (node.getNodeType() == RDFNodeType.Variable) {
                result.addAntecedentVar((XOWLVariableNode)node);
            }
            return node;
        }
        private RDFNode translateConsequentNode(RDFNode node) {
            if (node.getNodeType() == RDFNodeType.Variable)
                result.addConsequentVar((XOWLVariableNode)node);
            return node;
        }
        
        private boolean inspect(List<RDFTriple> buffer, LiteralExpression expression) {
            if (expression instanceof xowl.lang.actions.Invoke) return false;
            if (expression instanceof xowl.lang.actions.Execute) return false;
            if (expression instanceof xowl.lang.actions.Contains)return inspect(buffer, (xowl.lang.actions.Query)expression);
            if (expression instanceof xowl.lang.actions.ValueOf)return inspect(buffer, (xowl.lang.actions.Query)expression);
            if (expression instanceof xowl.lang.actions.CodeVariable) return true;
            if (expression instanceof xowl.lang.actions.QueryVariable) return true;
            if (expression instanceof xowl.lang.actions.ArrayElement) return inspect(buffer, (xowl.lang.actions.ArrayElement)expression);
            if (expression instanceof xowl.lang.interop.JavaInvokation) return false;
            if (expression instanceof xowl.lang.interop.ObjectField) return false;
            if (expression instanceof xowl.lang.interop.StaticField) return false;
            if (expression instanceof xowl.lang.actions.ArithmeticOperator_OR_BinaryBooleanOperator_OR_TestOperator) {
                xowl.lang.actions.ArithmeticOperator_OR_BinaryBooleanOperator_OR_TestOperator op = (xowl.lang.actions.ArithmeticOperator_OR_BinaryBooleanOperator_OR_TestOperator)expression;
                boolean left = inspect(buffer, op.getLhsAs(null));
                boolean right = inspect(buffer, op.getRhsAs(null));
                return (left && right);
            }
            if (expression instanceof xowl.lang.actions.BNot)
                return inspect(buffer, ((xowl.lang.actions.BNot)expression).getExpressionAs(expression));
            if (expression instanceof xowl.lang.actions.IRIOf) return false;
            if (expression instanceof xowl.lang.actions.LengthOf)
                return inspect(buffer, ((xowl.lang.actions.LengthOf)expression).getArray());
            if (expression instanceof xowl.lang.owl2.Literal) return true;
            return false;
        }
        
        private boolean inspect(List<RDFTriple> buffer, xowl.lang.actions.ArrayElement expression) {
            boolean left = inspect(buffer, expression.getArray());
            boolean right = inspect(buffer, expression.getIndex());
            return (left && right);
        }
        
        private boolean inspect(List<RDFTriple> buffer, Expression expression) {
            if (expression instanceof xowl.lang.actions.Invoke) return false;
            if (expression instanceof xowl.lang.actions.Execute) return false;
            if (expression instanceof xowl.lang.actions.Contains) return inspect(buffer, (xowl.lang.actions.Query)expression);
            if (expression instanceof xowl.lang.actions.ValueOf) return inspect(buffer, (xowl.lang.actions.Query)expression);
            if (expression instanceof xowl.lang.actions.ValuesOf) return inspect(buffer, (xowl.lang.actions.Query)expression);
            if (expression instanceof xowl.lang.actions.CodeVariable) return true;
            if (expression instanceof xowl.lang.actions.QueryVariable) return true;
            if (expression instanceof xowl.lang.actions.ArrayElement) return inspect(buffer, (xowl.lang.actions.ArrayElement)expression);
            if (expression instanceof xowl.lang.interop.JavaInvokation) return false;
            if (expression instanceof xowl.lang.interop.ObjectField) return false;
            if (expression instanceof xowl.lang.interop.StaticField) return false;
            if (expression instanceof xowl.lang.owl2.LiteralExpression)
                return inspect(buffer, (xowl.lang.owl2.LiteralExpression)expression);
            return false;
        }
        
        private boolean inspect(List<RDFTriple> buffer, Query expression) {
            
            return true;
        }
    }
}
